﻿#include <learn/painter/painter3d.h>

using namespace xi::painter;

Painter3D::Painter3D(int width, int height) : m_painter2D(width, height), m_zBuffer(width, height)
{
}

void Painter3D::refresh()
{
    m_painter2D.refresh();
    m_zBuffer.refresh(std::numeric_limits<float>::max());
}

void Painter3D::resize(int width, int height)
{
    m_painter2D.resize(width, height);
    m_zBuffer.resize(width, height);
}

TGA_Image Painter3D::frame()
{
    return m_painter2D.frame();
}

void Painter3D::set_frame(TGA_Image frame)
{
    m_painter2D.set_frame(frame);
    m_zBuffer.resize(frame->width, frame->height);
}

void Painter3D::fill_triangle(const Vec3f *xyz, const Vec2f *uv, const TGA_Image &texture)
{
    // 计算包围盒
    Vec2f rx(std::min(xyz[0].x(), std::min(xyz[1].x(), xyz[2].x())),
             std::max(xyz[0].x(), std::max(xyz[1].x(), xyz[2].x())));
    Vec2f ry(std::min(xyz[0].y(), std::min(xyz[1].y(), xyz[2].y())),
             std::max(xyz[0].y(), std::max(xyz[1].y(), xyz[2].y())));

    for (int x = rx[0]; x <= rx[1]; x++)
    {
        for (int y = ry[0]; y <= ry[1]; y++)
        {
            auto option = barycentric(Vec3f(float(x), float(y), 0.0f), xyz);
            if (option.has_value())
            {
                // 如果重心坐标有一个负值，则它在外侧
                auto coords = option.value();
                if (coords[0] < 0 || coords[1] < 0 || coords[2] < 0)
                    continue;

                // 计算 z 值
                float depth = xyz[0].z() * coords[0] + xyz[1].z() * coords[1] + xyz[2].z() * coords[2];
                if (depth < m_zBuffer.get(x, y))
                {
                    m_zBuffer.set(x, y, depth);
                    m_painter2D.frame().set(
                        x, y,
                        TGA_Color(texture.get((uv[0].x() * coords[0] + uv[1].x() * coords[1] + uv[2].x() * coords[2]) *
                                                  texture->width,
                                              (uv[0].y() * coords[0] + uv[1].y() * coords[1] + uv[2].y() * coords[2]) *
                                                  texture->height)));
                }
            }
        }
    }
}

void Painter3D::draw_object(const Mesh_Data &mesh, const TGA_Image &texture, std::shared_ptr<Camera> camera)
{
    for (int r = 0; r < mesh->indices.size(); r += 3)
    {
        int i = mesh->indices[r];
        int j = mesh->indices[r + 1];
        int k = mesh->indices[r + 2];
        auto normal = cross(mesh->vertices[j] - mesh->vertices[i], mesh->vertices[k] - mesh->vertices[i]);
        normal.normalize();

        // front 指向摄像机
        auto front = camera->front();
        if (dot(normal, front) > 0.0f)
        {
            std::vector<Vec3f> vertices({camera->screen(mesh->vertices[i]), camera->screen(mesh->vertices[j]),
                                         camera->screen(mesh->vertices[k])});
            std::vector<Vec2f> texcoords({mesh->texcoords[i], mesh->texcoords[j], mesh->texcoords[k]});
            fill_triangle(vertices.data(), texcoords.data(), texture);
        }
    }
}
